/*
 * PhotonRTOS础光实时操作系统 -- 体系结构定时器
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <asm/types.h>
#include <photon/init.h>
#include <photon/irq.h>
#include <photon/sched.h>
#include <photon/smp.h>
#include <photon/init_integrity.h>
#include <asm/arm_arch_timer.h>
#include <asm/arch_timer.h>
#include <asm/cputype.h>
#include <asm/io.h>
#include <autosar/counter_internal.h>
#include <photon/timex.h>

/*xilinx R5 TTC操作寄存器*/
#define TICK_INTERRUPT_ID		(36 + 32)

/* Definitions for peripheral PSU_TTC_0 */
#define XPAR_PSU_TTC_0_DEVICE_ID	0U
#define XPAR_PSU_TTC_0_BASEADDR		0XFF110000U
#define XPAR_PSU_TTC_0_TTC_CLK_FREQ_HZ	100000000U
#define XPAR_PSU_TTC_0_TTC_CLK_CLKSRC	0U

#define XPAR_PSU_TTC_1_DEVICE_ID	0U
#define XPAR_PSU_TTC_1_BASEADDR		0XFF120000U
#define XPAR_PSU_TTC_1_TTC_CLK_FREQ_HZ	100000000U
#define XPAR_PSU_TTC_1_TTC_CLK_CLKSRC	0U


#define XTTCPS_OPTION_EXTERNAL_CLK	0x00000001U 	/**< External clock source */
#define XTTCPS_OPTION_CLK_EDGE_NEG	0x00000002U	/**< Clock on trailing edge for
							  external clock*/
#define XTTCPS_OPTION_INTERVAL_MODE	0x00000004U	/**< Interval mode */
#define XTTCPS_OPTION_DECREMENT		0x00000008U	/**< Decrement the counter */
#define XTTCPS_OPTION_MATCH_MODE	0x00000010U	/**< Match mode */
#define XTTCPS_OPTION_WAVE_DISABLE	0x00000020U 	/**< No waveform output */
#define XTTCPS_OPTION_WAVE_POLARITY	0x00000040U	/**< Waveform polarity */

 /*
 * Register offsets from the base address of the device.
 *
 * @{
 */
/*每一个TTC内部有三个独立的定时器*/
#define XTTCPS_0_CLK_CNTRL_OFFSET	0x00000000U  /**< Clock Control Register */
#define XTTCPS_0_CNT_CNTRL_OFFSET	0x0000000CU  /**< Counter Control Register*/
#define XTTCPS_0_COUNT_VALUE_OFFSET	0x00000018U  /**< Current Counter Value */
#define XTTCPS_0_INTERVAL_VAL_OFFSET	0x00000024U  /**< Interval Count Value */
#define XTTCPS_0_MATCH_1_OFFSET		0x00000030U  /**< Match 1 value */
#define XTTCPS_0_MATCH_2_OFFSET		0x0000003CU  /**< Match 2 value */
#define XTTCPS_0_MATCH_3_OFFSET		0x00000048U  /**< Match 3 value */
#define XTTCPS_0_ISR_OFFSET		0x00000054U  /**< Interrupt Status Register */
#define XTTCPS_0_IER_OFFSET		0x00000060U  /**< Interrupt Enable Register */

#define XTTCPS_1_CLK_CNTRL_OFFSET	0x00000004U  /**< Clock Control Register */
#define XTTCPS_1_CNT_CNTRL_OFFSET	0x00000010U  /**< Counter Control Register*/
#define XTTCPS_1_COUNT_VALUE_OFFSET	0x0000001CU  /**< Current Counter Value */
#define XTTCPS_1_INTERVAL_VAL_OFFSET	0x00000028U  /**< Interval Count Value */
#define XTTCPS_1_MATCH_1_OFFSET		0x00000034U  /**< Match 1 value */
#define XTTCPS_1_MATCH_2_OFFSET		0x00000040U  /**< Match 2 value */
#define XTTCPS_1_MATCH_3_OFFSET		0x0000004CU  /**< Match 3 value */
#define XTTCPS_1_ISR_OFFSET		0x00000058U  /**< Interrupt Status Register */
#define XTTCPS_1_IER_OFFSET		0x00000064U  /**< Interrupt Enable Register */

#define XTTCPS_2_CLK_CNTRL_OFFSET	0x00000008U  /**< Clock Control Register */
#define XTTCPS_2_CNT_CNTRL_OFFSET	0x00000014U  /**< Counter Control Register*/
#define XTTCPS_2_COUNT_VALUE_OFFSET	0x00000020U  /**< Current Counter Value */
#define XTTCPS_2_INTERVAL_VAL_OFFSET	0x0000002CU  /**< Interval Count Value */
#define XTTCPS_2_MATCH_1_OFFSET		0x00000038U  /**< Match 1 value */
#define XTTCPS_2_MATCH_2_OFFSET		0x00000044U  /**< Match 2 value */
#define XTTCPS_2_MATCH_3_OFFSET		0x00000050U  /**< Match 3 value */
#define XTTCPS_2_ISR_OFFSET		0x0000005CU  /**< Interrupt Status Register */
#define XTTCPS_2_IER_OFFSET		0x00000068U  /**< Interrupt Enable Register */
/* @} */

/** @name Clock Control Register
 * Clock Control Register definitions
 * @{
 */
#define XTTCPS_CLK_CNTRL_PS_EN_MASK	0x00000001U  /**< Prescale enable */
#define XTTCPS_CLK_CNTRL_PS_VAL_MASK	0x0000001EU  /**< Prescale value */
#define XTTCPS_CLK_CNTRL_PS_VAL_SHIFT	1U  /**< Prescale shift */
#define XTTCPS_CLK_CNTRL_PS_DISABLE	16U  /**< Prescale disable */
#define XTTCPS_CLK_CNTRL_SRC_MASK	0x00000020U  /**< Clock source */
#define XTTCPS_CLK_CNTRL_EXT_EDGE_MASK	0x00000040U  /**< External Clock edge */
/* @} */

/** @name Counter Control Register
 * Counter Control Register definitions
 * @{
 */
#define XTTCPS_CNT_CNTRL_DIS_MASK	0x00000001U /**< Disable the counter */
#define XTTCPS_CNT_CNTRL_INT_MASK	0x00000002U /**< Interval mode */
#define XTTCPS_CNT_CNTRL_DECR_MASK	0x00000004U /**< Decrement mode */
#define XTTCPS_CNT_CNTRL_MATCH_MASK	0x00000008U /**< Match mode */
#define XTTCPS_CNT_CNTRL_RST_MASK	0x00000010U /**< Reset counter */
#define XTTCPS_CNT_CNTRL_EN_WAVE_MASK	0x00000020U /**< Enable waveform */
#define XTTCPS_CNT_CNTRL_POL_WAVE_MASK	0x00000040U /**< Waveform polarity */
#define XTTCPS_CNT_CNTRL_RESET_VALUE	0x00000021U /**< Reset value */


#define XTTCPS_IXR_INTERVAL_MASK	0x00000001U  /**< Interval Interrupt */
#define XTTCPS_IXR_MATCH_1_MASK		0x00000002U  /**< Match 1 Interrupt */
#define XTTCPS_IXR_MATCH_2_MASK		0x00000004U  /**< Match 2 Interrupt */
#define XTTCPS_IXR_MATCH_3_MASK		0x00000008U  /**< Match 3 Interrupt */
#define XTTCPS_IXR_CNT_OVR_MASK		0x00000010U  /**< Counter Overflow */
#define XTTCPS_IXR_ALL_MASK		0x0000001FU  /**< All valid Interrupts */
/* @} */

#define XTTCPS_MAX_INTERVAL_COUNT 0xFFFFFFFFU

static void xilinx_ttc_init(uintptr_t regbase)
{
	uint32_t val;

	/*初始化计数器0*/
	/*判读是否已经开始*/
	val = readl(regbase + XTTCPS_0_CNT_CNTRL_OFFSET);

	writel(val | XTTCPS_CNT_CNTRL_DIS_MASK, regbase + XTTCPS_0_CNT_CNTRL_OFFSET);/*stop ttc*/
	writel(XTTCPS_CNT_CNTRL_RESET_VALUE, regbase + XTTCPS_0_CNT_CNTRL_OFFSET);/*reset ttc*/

	writel(0, regbase + XTTCPS_0_CLK_CNTRL_OFFSET);
	writel(0, regbase + XTTCPS_0_INTERVAL_VAL_OFFSET);
	writel(0, regbase + XTTCPS_0_MATCH_1_OFFSET);

	writel(0, regbase + XTTCPS_0_MATCH_2_OFFSET);
	writel(0, regbase + XTTCPS_0_MATCH_3_OFFSET);
	writel(0, regbase + XTTCPS_0_IER_OFFSET);
	writel(0, regbase + XTTCPS_0_ISR_OFFSET);

	val = readl(regbase + XTTCPS_0_CNT_CNTRL_OFFSET);
	writel(val | XTTCPS_CNT_CNTRL_RST_MASK, regbase + XTTCPS_0_CNT_CNTRL_OFFSET);

	/*初始化计数器1*/

	val = readl(regbase + XTTCPS_1_CNT_CNTRL_OFFSET);

	writel(val | XTTCPS_CNT_CNTRL_DIS_MASK, regbase + XTTCPS_1_CNT_CNTRL_OFFSET);/*stop ttc*/
	writel(XTTCPS_CNT_CNTRL_RESET_VALUE, regbase + XTTCPS_1_CNT_CNTRL_OFFSET);/*reset ttc*/

	writel(0, regbase + XTTCPS_1_CLK_CNTRL_OFFSET);
	writel(0, regbase + XTTCPS_1_INTERVAL_VAL_OFFSET);
	writel(0, regbase + XTTCPS_1_MATCH_1_OFFSET);

	writel(0, regbase + XTTCPS_1_MATCH_2_OFFSET);
	writel(0, regbase + XTTCPS_1_MATCH_3_OFFSET);
	writel(0, regbase + XTTCPS_1_IER_OFFSET);
	writel(0, regbase + XTTCPS_1_ISR_OFFSET);

	val = readl(regbase + XTTCPS_1_CNT_CNTRL_OFFSET);
	writel(val | XTTCPS_CNT_CNTRL_RST_MASK, regbase + XTTCPS_1_CNT_CNTRL_OFFSET);

	/*初始化计数器2*/

	val = readl(regbase + XTTCPS_2_CNT_CNTRL_OFFSET);

	writel(val | XTTCPS_CNT_CNTRL_DIS_MASK, regbase + XTTCPS_2_CNT_CNTRL_OFFSET);/*stop ttc*/
	writel(XTTCPS_CNT_CNTRL_RESET_VALUE, regbase + XTTCPS_2_CNT_CNTRL_OFFSET);/*reset ttc*/

	writel(0, regbase + XTTCPS_2_CLK_CNTRL_OFFSET);
	writel(0, regbase + XTTCPS_2_INTERVAL_VAL_OFFSET);
	writel(0, regbase + XTTCPS_2_MATCH_1_OFFSET);

	writel(0, regbase + XTTCPS_2_MATCH_2_OFFSET);
	writel(0, regbase + XTTCPS_2_MATCH_3_OFFSET);
	writel(0, regbase + XTTCPS_2_IER_OFFSET);
	writel(0, regbase + XTTCPS_2_ISR_OFFSET);

	val = readl(regbase + XTTCPS_2_CNT_CNTRL_OFFSET);
	writel(val | XTTCPS_CNT_CNTRL_RST_MASK, regbase + XTTCPS_2_CNT_CNTRL_OFFSET);
}

static void xilinx_ttc_set(uintptr_t regbase, uint32_t timer_rate)
{
	uint32_t s32interval = 0;
	uint32_t s32prescaler = 0;
	uint32_t i,temp;

	temp  = timer_rate / (uint32_t)HZ;

	if( temp < (uint32_t)4)
	{
		s32interval = XTTCPS_MAX_INTERVAL_COUNT;
		s32prescaler = 0xff;
	}
	else if(temp < (uint32_t)0x10000)
	{
		s32interval = temp;
		s32prescaler = XTTCPS_CLK_CNTRL_PS_DISABLE;
	}
	else
	{
		for (i = 0; i < XTTCPS_CLK_CNTRL_PS_DISABLE; i++)
		{
			temp = timer_rate/ ((uint32_t)HZ * (1U << (i + 1U)));

			/*
			 * The first value less than 2^16 is the best bet
			 */
			if (((uint32_t)65536U) > temp) {
				/*
				 * Set the values appropriately
				 */
				s32interval = temp;
				s32prescaler = i;
				break;
			}
		}

	}
	/*设置定时器0*/
	writel(s32interval, regbase + XTTCPS_0_INTERVAL_VAL_OFFSET);
	temp = readl(regbase + XTTCPS_0_INTERVAL_VAL_OFFSET);

	temp = readl(regbase + XTTCPS_0_CLK_CNTRL_OFFSET);
	temp &=	(~(XTTCPS_CLK_CNTRL_PS_VAL_MASK | XTTCPS_CLK_CNTRL_PS_EN_MASK));

	temp |= (uint32_t)(((uint32_t)s32prescaler << (uint32_t)XTTCPS_CLK_CNTRL_PS_VAL_SHIFT) &
		(uint32_t)XTTCPS_CLK_CNTRL_PS_VAL_MASK);
	temp |= (uint32_t)XTTCPS_CLK_CNTRL_PS_EN_MASK;
	writel(temp, regbase + XTTCPS_0_CLK_CNTRL_OFFSET);


	temp = readl(regbase + XTTCPS_0_CNT_CNTRL_OFFSET);
	temp |= XTTCPS_CNT_CNTRL_INT_MASK;
	temp |= XTTCPS_CNT_CNTRL_EN_WAVE_MASK;
	writel(temp, regbase + XTTCPS_0_CNT_CNTRL_OFFSET);

	temp = readl(regbase + XTTCPS_0_CNT_CNTRL_OFFSET);
	temp &= (~XTTCPS_CNT_CNTRL_DIS_MASK);
	writel(temp, regbase + XTTCPS_0_CNT_CNTRL_OFFSET);

	/*设置定时器1*/
	writel(s32interval, regbase + XTTCPS_1_INTERVAL_VAL_OFFSET);
	temp = readl(regbase + XTTCPS_1_INTERVAL_VAL_OFFSET);

	temp = readl(regbase + XTTCPS_1_CLK_CNTRL_OFFSET);
	temp &=	(~(XTTCPS_CLK_CNTRL_PS_VAL_MASK | XTTCPS_CLK_CNTRL_PS_EN_MASK));

	/*这里启动预分频*/
	temp |= (uint32_t)(((uint32_t)s32prescaler << (uint32_t)XTTCPS_CLK_CNTRL_PS_VAL_SHIFT) &
		(uint32_t)XTTCPS_CLK_CNTRL_PS_VAL_MASK);
	temp |= (uint32_t)XTTCPS_CLK_CNTRL_PS_EN_MASK;
	writel(temp, regbase + XTTCPS_1_CLK_CNTRL_OFFSET);

	/*这里设置计数控制器*/
	temp = readl(regbase + XTTCPS_1_CNT_CNTRL_OFFSET);
	temp |= XTTCPS_CNT_CNTRL_INT_MASK;
	temp |= XTTCPS_CNT_CNTRL_EN_WAVE_MASK;
	writel(temp, regbase + XTTCPS_1_CNT_CNTRL_OFFSET);

	temp = readl(regbase + XTTCPS_1_CNT_CNTRL_OFFSET);
	temp &= (~XTTCPS_CNT_CNTRL_DIS_MASK);
	writel(temp, regbase + XTTCPS_1_CNT_CNTRL_OFFSET);

	/*设置定时器2*/
	writel(s32interval, regbase + XTTCPS_2_INTERVAL_VAL_OFFSET);
	temp = readl(regbase + XTTCPS_2_INTERVAL_VAL_OFFSET);

	temp = readl(regbase + XTTCPS_2_CLK_CNTRL_OFFSET);
	temp &=	(~(XTTCPS_CLK_CNTRL_PS_VAL_MASK | XTTCPS_CLK_CNTRL_PS_EN_MASK));

	temp |= (uint32_t)(((uint32_t)(s32prescaler * 2) << (uint32_t)XTTCPS_CLK_CNTRL_PS_VAL_SHIFT) &
		(uint32_t)XTTCPS_CLK_CNTRL_PS_VAL_MASK);
	temp |= (uint32_t)XTTCPS_CLK_CNTRL_PS_EN_MASK;
	writel(temp, regbase + XTTCPS_2_CLK_CNTRL_OFFSET);


	temp = readl(regbase + XTTCPS_2_CNT_CNTRL_OFFSET);
	temp |= XTTCPS_CNT_CNTRL_INT_MASK;
	temp |= XTTCPS_CNT_CNTRL_EN_WAVE_MASK;
	writel(temp, regbase + XTTCPS_2_CNT_CNTRL_OFFSET);

	temp = readl(regbase + XTTCPS_2_CNT_CNTRL_OFFSET);
	temp &= (~XTTCPS_CNT_CNTRL_DIS_MASK);
	writel(temp, regbase + XTTCPS_2_CNT_CNTRL_OFFSET);
}

static void xilinx_ttc_enirq(uintptr_t regbase)
{
	uint32_t temp;
	/*打开定时器0中断*/
	temp = readl(regbase + XTTCPS_0_IER_OFFSET);
	temp |= XTTCPS_IXR_INTERVAL_MASK;
	writel(temp, regbase + XTTCPS_0_IER_OFFSET);
	/*打开定时器1中断*/
	temp = readl(regbase + XTTCPS_1_IER_OFFSET);
	temp |= XTTCPS_IXR_INTERVAL_MASK;
	writel(temp, regbase + XTTCPS_1_IER_OFFSET);
	/*打开定时器2中断*/
	temp = readl(regbase + XTTCPS_2_IER_OFFSET);
	temp |= XTTCPS_IXR_INTERVAL_MASK;
	writel(temp, regbase + XTTCPS_2_IER_OFFSET);
}

static uint32_t xilinx_ttc_rdint(uintptr_t  regbase, uint32_t countid)
{
	uint32_t res;
	switch (countid)
	{
	case 0:
		res = readl(regbase + XTTCPS_0_ISR_OFFSET);
		break;
	case 1:
		res = readl(regbase + XTTCPS_1_ISR_OFFSET);
		break;
	case 2:
		res = readl(regbase + XTTCPS_2_ISR_OFFSET);
		break;
	default:
		/*不应该执行到这里，每个TTC只有三个定时器*/
		BUG();
		break;
	}
	return res;
}

static void xilinx_ttc0_int_status(uint32_t countid)
{
	xilinx_ttc_rdint(XPAR_PSU_TTC_0_BASEADDR, countid);
}

static void xilinx_ttc1_int_status(uint32_t countid)
{
	xilinx_ttc_rdint(XPAR_PSU_TTC_1_BASEADDR, countid);
}

int xilinx_ttc_int_status(int cpu, uint32_t countid)
{
	if (0 == cpu)
		xilinx_ttc0_int_status(countid);
	else if (1 == cpu)
		xilinx_ttc1_int_status(countid);

	return 0;
}

/*
 * @brief 定时器相关操作
 *
 */
static uint32_t arch_timer_rate;

static void
arch_timer_detect_rate(void)
{
	arch_timer_rate = arch_timer_get_cntfrq();

	pr_debug("定时器初始化完毕，时钟频率：%d。\n",arch_timer_rate);
}

static int count = 0;
static int count1 = 0;

/*这个主定时器中断，就默认不嵌套了*/
static void do_timer_handler(void)
{
	int cpu = read_cpuid() & 0xff;

	if (0 == cpu) {
		if (count % HZ == 0)
			pr_debug("cortexr5_timer_handler cpu %d\n", cpu);
		count++;
	} else {
		if (count1 % HZ == 0)
			pr_debug("cortexr5_timer_handler cpu %d\n", cpu);
		count1++;
	}

	if (cpu == 0) {
		add_jiffies_64(1);
	}

	IncrementHardwareCounter(cpu);
	/**
	 * 每秒检查中断风暴情况
	 */
	if (((uint32_t)get_jiffies_64()) % HZ == 0) {
		check_irq_storm();
	}
	xilinx_ttc_int_status(cpu, 0);/* 清除中断状态 */

	current->live_tick--;
	if (current->live_tick <= 0 && current->sched_policy == SCHED_TIMESLICE) {

		set_task_need_resched(current);
	}

}

/*使用格式，ttc0_0表示R5中第0个TTC中的第0个定时器*/
ISR_FUN(cortexr5_timer_ttc0_0);

ISR_FUN(cortexr5_timer_ttc0_0)
{
	do_timer_handler();
}

static void __cortexr5_timer_ttc0_setup(void)
{
	xilinx_ttc_init(XPAR_PSU_TTC_0_BASEADDR);
	xilinx_ttc_set(XPAR_PSU_TTC_0_BASEADDR, arch_timer_rate);
	/* 中断已经使能 */
	xilinx_ttc_enirq(XPAR_PSU_TTC_0_BASEADDR);
}

void init_time_arch(void);
void init_time_arch(void)
{
	arch_timer_detect_rate();
}

void init_timer_arch(void);
void init_timer_arch(void)
{
	MODULE_INIT(init_timer_arch);
	__cortexr5_timer_ttc0_setup();
}

ISR_FUN(cortexr5_timer_ttc1_0);

ISR_FUN(cortexr5_timer_ttc1_0)
{
	do_timer_handler();
}

INTRRUPT(72);

INTRRUPT(72)
{
	int cpu = read_cpuid() & 0xff;
	printk("\n一类中断调用！\n");
	xilinx_ttc_int_status(cpu, 1);
	mask_irq(72);
}
static void __cortexr5_timer_ttc1_setup(void)
{
	xilinx_ttc_init(XPAR_PSU_TTC_1_BASEADDR);
	xilinx_ttc_set(XPAR_PSU_TTC_1_BASEADDR, arch_timer_rate);

	xilinx_ttc_enirq(XPAR_PSU_TTC_1_BASEADDR);
}

static void init_timer_ttc1_cortexr5(void)
{
	__cortexr5_timer_ttc1_setup();
}

void arch_timer_secondary_init(void)
{
#if defined(CONFIG_ARCH_XILINX_CORTEXR5)
	init_timer_ttc1_cortexr5();
#else
#	error arch_timer_secondary_init not support!
#endif
}

/**
 * 声明中断相关信息
 *
 */
DeclareInterrupt(cortexr5_timer_ttc0_0, 68, HWIRQ_TYPE_EDGE_RISING, 1000, 1, false);
#if defined (CONFIG_AUTOSAR_INTERRUPT_NESTING_TEST)
DeclareInterrupt(cortexr5_timer_ttc0_1, 69, HWIRQ_TYPE_EDGE_RISING, 1000, 7, true);
DeclareInterrupt(cortexr5_timer_ttc0_2, 70, HWIRQ_TYPE_EDGE_RISING, 1000, 8, true);
#endif
DeclareInterrupt(cortexr5_timer_ttc1_0, 71, HWIRQ_TYPE_EDGE_RISING, 2000, 10, 1);

struct irq_desc *os_irq_desc[NR_IRQS] = {
	[0] = &ISR_ATTR(cortexr5_timer_ttc0_0),
#if defined (CONFIG_AUTOSAR_INTERRUPT_NESTING_TEST)
	[1] = &ISR_ATTR(cortexr5_timer_ttc0_1),
	[2] = &ISR_ATTR(cortexr5_timer_ttc0_2),
#endif
	[3] = &ISR_ATTR(cortexr5_timer_ttc1_0),
	NULL,
};
